﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Moki;

namespace Moki.Core
{
    /// <summary>
    /// Encapulates stub parameters (method, arguments etc.) and consecutive stub results list.
    /// </summary>
    internal class StubContext
    {
        public StubContext(MethodInfo method, Arguments arguments)
        {
            Method = method;
            Arguments = arguments;
            Results = new List<StubResult>();
        }

        public StubContext(MethodInfo method)
            : this(method, new Arguments())
        {
            Method = method;
        }

        public MethodInfo Method { get; set; }
        public Arguments Arguments { get; set; }
        public bool IgnoreSet { get; set; }
        private List<StubResult> Results { get; set; }
        private int _currentIndex { get; set; }

        /// <summary>
        /// Current stub result.
        /// </summary>
        public StubResult CurrentResult
        {
            get { return Results[_currentIndex]; }
        }

        /// <summary>
        /// Return value of result before current.
        /// </summary>
        public object LastReturnValue { get; set; }

        public bool HasResult
        {
            get { return Results.Count > 0; }
        }

        public void AddResultDefault()
        {
            StubResult result = new StubResult();
            Results.Add(result);
        }

        public void AddResultDefault(Type type)
        {
            StubResult result = new StubResult();
            if (type.IsValueType && type != typeof(void))
                result.ReturnValue = Activator.CreateInstance(type);
            Results.Add(result);
        }

        public void AddResultValue(object value)
        {
            StubResult result = new StubResult();
            result.ReturnValue = value;

            Results.Add(result);
        }

        public void AddCallBackToResult(Action<Arguments> action)
        {
            if (Results.Count == 0)
                Results.Add(new StubResult());

            Results.LastOrDefault().CallBackAction = action;
        }

        public void AddEventArgs(object[] eventArgs)
        {
            if (Results.Count == 0)
                Results.Add(new StubResult());

            Results.LastOrDefault().EventArgs = eventArgs;
        }

        public void AddEventToResult(Delegate eventDelegate)
        {
            if (Results.Count == 0)
                Results.Add(new StubResult());

            Results.LastOrDefault().Event = eventDelegate;
        }

        public void AddResultImplementation(Delegate implementation)
        {
            StubResult result = new StubResult();
            result.Implementation = implementation;

            Results.Add(result);
        }

        public void AddResultThrowException(Delegate implementation)
        {
            StubResult result = new StubResult();
            result.Implementation = implementation;
            result.ThrowsException = true;
            Results.Add(result);
        }

        /// <summary>
        /// Invoke current result implementation and sets next stub result. 
        /// </summary>
        public void Invoke()
        {
            Results[_currentIndex].Invoke(Arguments);
            LastReturnValue = Results[_currentIndex].ReturnValue;
            NextResult();
        }

        /// <summary>
        /// Sets next stub result.
        /// </summary>
        /// <returns></returns>
        private bool NextResult()
        {
            if (_currentIndex < Results.Count - 1)
            {
                _currentIndex++;
                return true;
            }
            else
                return false;
        }

        public override bool Equals(object obj)
        {
            if (obj is StubContext)
            {
                var call = (StubContext)obj;
                return this.Method.Name == call.Method.Name && this.Arguments.Matches(call.Arguments);
            }
            else
                return false;
        }
    }
}
